// 版权所有2016 etcd作者
// 
// 根据Apache许可证2.0版（以下简称“许可证”）获得许可；
// 除非遵守许可证，否则不得使用此文件。
// 您可以通过
// 
// www.apache.org/licenses/LICENSE-2.0 
// 
// 除非适用法律要求或书面同意，否则根据许可证分发的软件
// 按“原样”分发，
// 无任何明示或暗示的保证或条件。
// 请参阅许可证，了解管理许可的特定语言和
// 许可证下的限制。

package clientv3

import pb "go.etcd.io/etcd/api/v3/etcdserverpb"

type opType int

const (
	// 默认Op的opType为0，这是无效的。
	tRange opType = iota + 1
	tPut
	tDeleteRange
	tTxn
)

var noPrefixEnd = []byte{0}

// Op表示kv可以执行的操作。
type Op struct {
	t   opType
	key []byte
	end []byte

	// 对于范围
	limit        int64
	sort         *SortOption
	serializable bool
	keysOnly     bool
	countOnly    bool
	minModRev    int64
	maxModRev    int64
	minCreateRev int64
	maxCreateRev int64

	// 对于范围，监视
	rev int64

	// 对于监视，放置，删除
	prevKV bool

	// 对于监视
	// 默认情况下应禁用分段
	// 如果为真，则在总数超过
	// /--最大请求字节数时分割监视事件标志值+512字节
	fragment bool

	// 用于put 
	ignoreValue bool
	ignoreLease bool

	// progressNotify用于进度更新。
	progressNotify bool
	// createdNotify用于已创建事件
	createdNotify bool
	// 用于观察者的筛选器
	filterPut    bool
	filterDelete bool

	// 用于put 
	val     []byte
	leaseID LeaseID

	// txn 
	cmps    []Cmp
	thenOps []Op
	elseOps []Op

	isOptsWithFromKey bool
	isOptsWithPrefix  bool
}

// 如果“Op”类型为事务，则访问器/变异器

// IsTxn返回true。
func (op Op) IsTxn() bool {
	return op.t == tTxn
}

// Txn返回比较（if）运算、“then”运算和“else”运算。
func (op Op) Txn() ([]Cmp, []Op, []Op) {
	return op.cmps, op.thenOps, op.elseOps
}

// KeyBytes返回保存Op键的字节片。
func (op Op) KeyBytes() []byte { return op.key }

// WithKeyBytes设置Op键的字节片。
func (op *Op) WithKeyBytes(key []byte) { op.key = key }

// RangeBytes返回保留Op范围结束的字节片（如果有）。
func (op Op) RangeBytes() []byte { return op.end }

// Rev返回请求的版本（如果有）。
func (op Op) Rev() int64 { return op.rev }

// IsPut在操作为Put时返回true。
func (op Op) IsPut() bool { return op.t == tPut }

// IsGet在操作为Get时返回true。
func (op Op) IsGet() bool { return op.t == tRange }

// IsDelete在操作为删除时返回true。
func (op Op) IsDelete() bool { return op.t == tDeleteRange }

// IsSerializable如果可序列化字段为true，则返回true。
func (op Op) IsSerializable() bool { return op.serializable }

// IsKeysOnly返回是否设置了keysOnly。
func (op Op) IsKeysOnly() bool { return op.keysOnly }

// IsCountOnly返回是否设置了countOnly。
func (op Op) IsCountOnly() bool { return op.countOnly }

// MinModRev返回操作的最小修改版本。
func (op Op) MinModRev() int64 { return op.minModRev }

// MaxModRev返回操作的最大修改版本。
func (op Op) MaxModRev() int64 { return op.maxModRev }

// MinCreateRev返回操作的最小创建版本。
func (op Op) MinCreateRev() int64 { return op.minCreateRev }

// MaxCreateRev返回操作的最大创建版本。
func (op Op) MaxCreateRev() int64 { return op.maxCreateRev }

// WithRangeBytes为Op的范围结束设置字节片。
func (op *Op) WithRangeBytes(end []byte) { op.end = end }

// ValueBytes返回保存Op值的字节片（如果有）。
func (op Op) ValueBytes() []byte { return op.val }

// WithValueBytes设置Op值的字节片。
func (op *Op) WithValueBytes(v []byte) { op.val = v }

func (op Op) toRangeRequest() *pb.RangeRequest {
	if op.t != tRange {
		panic("op.t != tRange")
	}
	r := &pb.RangeRequest{
		Key:               op.key,
		RangeEnd:          op.end,
		Limit:             op.limit,
		Revision:          op.rev,
		Serializable:      op.serializable,
		KeysOnly:          op.keysOnly,
		CountOnly:         op.countOnly,
		MinModRevision:    op.minModRev,
		MaxModRevision:    op.maxModRev,
		MinCreateRevision: op.minCreateRev,
		MaxCreateRevision: op.maxCreateRev,
	}
	if op.sort != nil {
		r.SortOrder = pb.RangeRequest_SortOrder(op.sort.Order)
		r.SortTarget = pb.RangeRequest_SortTarget(op.sort.Target)
	}
	return r
}

func (op Op) toTxnRequest() *pb.TxnRequest {
	thenOps := make([]*pb.RequestOp, len(op.thenOps))
	for i, tOp := range op.thenOps {
		thenOps[i] = tOp.toRequestOp()
	}
	elseOps := make([]*pb.RequestOp, len(op.elseOps))
	for i, eOp := range op.elseOps {
		elseOps[i] = eOp.toRequestOp()
	}
	cmps := make([]*pb.Compare, len(op.cmps))
	for i := range op.cmps {
		cmps[i] = (*pb.Compare)(&op.cmps[i])
	}
	return &pb.TxnRequest{Compare: cmps, Success: thenOps, Failure: elseOps}
}

func (op Op) toRequestOp() *pb.RequestOp {
	switch op.t {
	case tRange:
		return &pb.RequestOp{Request: &pb.RequestOp_RequestRange{RequestRange: op.toRangeRequest()}}
	case tPut:
		r := &pb.PutRequest{Key: op.key, Value: op.val, Lease: int64(op.leaseID), PrevKv: op.prevKV, IgnoreValue: op.ignoreValue, IgnoreLease: op.ignoreLease}
		return &pb.RequestOp{Request: &pb.RequestOp_RequestPut{RequestPut: r}}
	case tDeleteRange:
		r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end, PrevKv: op.prevKV}
		return &pb.RequestOp{Request: &pb.RequestOp_RequestDeleteRange{RequestDeleteRange: r}}
	case tTxn:
		return &pb.RequestOp{Request: &pb.RequestOp_RequestTxn{RequestTxn: op.toTxnRequest()}}
	default:
		panic("Unknown Op")
	}
}

func (op Op) isWrite() bool {
	if op.t == tTxn {
		for _, tOp := range op.thenOps {
			if tOp.isWrite() {
				return true
			}
		}
		for _, tOp := range op.elseOps {
			if tOp.isWrite() {
				return true
			}
		}
		return false
	}
	return op.t != tRange
}

func NewOp() *Op {
	return &Op{key: []byte("")}
}

// OpGet根据给定的键和操作选项返回“get”操作。
func OpGet(key string, opts ...OpOption) Op {
	// WithPrefix和WithFromKey不同时支持
	if IsOptsWithPrefix(opts) && IsOptsWithFromKey(opts) {
		panic("`WithPrefix` and `WithFromKey` cannot be set at the same time, choose one")
	}
	ret := Op{t: tRange, key: []byte(key)}
	ret.applyOpts(opts)
	return ret
}

// OpDelete根据给定的键和操作选项返回“delete”操作。
func OpDelete(key string, opts ...OpOption) Op {
	// WithPrefix和WithFromKey不同时支持
	if IsOptsWithPrefix(opts) && IsOptsWithFromKey(opts) {
		panic("`WithPrefix` and `WithFromKey` cannot be set at the same time, choose one")
	}
	ret := Op{t: tDeleteRange, key: []byte(key)}
	ret.applyOpts(opts)
	switch {
	case ret.leaseID != 0:
		panic("unexpected lease in delete")
	case ret.limit != 0:
		panic("unexpected limit in delete")
	case ret.rev != 0:
		panic("unexpected revision in delete")
	case ret.sort != nil:
		panic("unexpected sort in delete")
	case ret.serializable:
		panic("unexpected serializable in delete")
	case ret.countOnly:
		panic("unexpected countOnly in delete")
	case ret.minModRev != 0, ret.maxModRev != 0:
		panic("unexpected mod revision filter in delete")
	case ret.minCreateRev != 0, ret.maxCreateRev != 0:
		panic("unexpected create revision filter in delete")
	case ret.filterDelete, ret.filterPut:
		panic("unexpected filter in delete")
	case ret.createdNotify:
		panic("unexpected createdNotify in delete")
	}
	return ret
}

// OpPut根据给定的键值和操作选项返回“put”操作。
func OpPut(key, val string, opts ...OpOption) Op {
	ret := Op{t: tPut, key: []byte(key), val: []byte(val)}
	ret.applyOpts(opts)
	switch {
	case ret.end != nil:
		panic("unexpected range in put")
	case ret.limit != 0:
		panic("unexpected limit in put")
	case ret.rev != 0:
		panic("unexpected revision in put")
	case ret.sort != nil:
		panic("unexpected sort in put")
	case ret.serializable:
		panic("unexpected serializable in put")
	case ret.countOnly:
		panic("unexpected countOnly in put")
	case ret.minModRev != 0, ret.maxModRev != 0:
		panic("unexpected mod revision filter in put")
	case ret.minCreateRev != 0, ret.maxCreateRev != 0:
		panic("unexpected create revision filter in put")
	case ret.filterDelete, ret.filterPut:
		panic("unexpected filter in put")
	case ret.createdNotify:
		panic("unexpected createdNotify in put")
	}
	return ret
}

// OpTxn根据给定的事务条件返回“txn”操作。
func OpTxn(cmps []Cmp, thenOps []Op, elseOps []Op) Op {
	return Op{t: tTxn, cmps: cmps, thenOps: thenOps, elseOps: elseOps}
}

func opWatch(key string, opts ...OpOption) Op {
	ret := Op{t: tRange, key: []byte(key)}
	ret.applyOpts(opts)
	switch {
	case ret.leaseID != 0:
		panic("unexpected lease in watch")
	case ret.limit != 0:
		panic("unexpected limit in watch")
	case ret.sort != nil:
		panic("unexpected sort in watch")
	case ret.serializable:
		panic("unexpected serializable in watch")
	case ret.countOnly:
		panic("unexpected countOnly in watch")
	case ret.minModRev != 0, ret.maxModRev != 0:
		panic("unexpected mod revision filter in watch")
	case ret.minCreateRev != 0, ret.maxCreateRev != 0:
		panic("unexpected create revision filter in watch")
	}
	return ret
}

func (op *Op) applyOpts(opts []OpOption) {
	for _, opt := range opts {
		opt(op)
	}
}

// OPTION配置诸如Get、Put、Delete之类的操作。
type OpOption func(*Op)

// WithLease将租约ID附加到“Put”请求中的密钥。
func WithLease(leaseID LeaseID) OpOption {
	return func(op *Op) { op.leaseID = leaseID }
}

// WithLimit限制从“Get”请求返回的结果数。
// 如果WithLimit的限制为0，则视为无限制。
func WithLimit(n int64) OpOption { return func(op *Op) { op.limit = n } }

// WithRev指定“Get”请求的存储版本。
// 或开始修改“监视”请求。
func WithRev(rev int64) OpOption { return func(op *Op) { op.rev = rev } }

// with-sort指定“Get”请求中的顺序。它还要求指定
// /'WithRange'和/或'WithPrefix'。
// /'target'指定要排序的目标：键、版本、修订、值。
// /'订单'可以是'SortNone'，'SortAscend'，'sortdescent'。
func WithSort(target SortTarget, order SortOrder) OpOption {
	return func(op *Op) {
		if target == SortByKey && order == SortAscend {
			// 如果订单！=SortOne，服务器获取整个密钥空间
			// 然后应用排序和限制（如果提供）。
			// 由于默认情况下服务器返回按关键字
			// 按字典顺序升序排序的结果，因此如果目标为SortByKey，则客户端应忽略
			// SortOrder。
			order = SortNone
		}
		op.sort = &SortOption{target, order}
	}
}

// GetPrefixRangeEnd获取前缀的范围结尾。
// “Get（foo，WithPrefix（））”等于“Get（foo，WithRange（GetPrefixRangeEnd（foo））。
func GetPrefixRangeEnd(prefix string) string {
	return string(getPrefix([]byte(prefix)))
}

func getPrefix(key []byte) []byte {
	end := make([]byte, len(key))
	copy(end, key)
	for i := len(end) - 1; i >= 0; i-- {
		if end[i] < 0xff {
			end[i] = end[i] + 1
			end = end[:i+1]
			return end
		}
	}
	// 下一个前缀不存在（例如0xffff）；
	// 默认为WithFromKey策略
	return noPrefixEnd
}

// WithPrefix启用“Get”、“Delete”或“Watch”请求来操作
// 在前缀匹配的键上。例如，“Get（foo，WithPrefix（））”
// 可以返回“foo1”、“foo2”等。
func WithPrefix() OpOption {
	return func(op *Op) {
		if len(op.key) == 0 {
			op.key, op.end = []byte{0}, []byte{0}
			return
		}
		op.end = getPrefix(op.key)
		op.isOptsWithPrefix = true
	}
}

// WithRange指定“获取”、“删除”、“监视”请求的范围。
// 例如，带有“WithRange（end）”的“Get”请求返回
// 范围内的键[key，end）.
// endKey必须在字典上大于start key.
func WithRange(endKey string) OpOption {
	return func(op *Op) { op.end = []byte(endKey) }
}

// WithFromKey指定'Get'，Delete'，'Watch'请求的范围
// 等于或大于参数中的键.
func WithFromKey() OpOption {
	return func(op *Op) {
		if len(op.key) == 0 {
			op.key = []byte{0}
		}
		op.end = []byte("\x00")
		op.isOptsWithFromKey = true
	}
}

// WithSerializable使'Get'请求可序列化.默认情况下，
// 它是可线性化的。可串行化的请求更适合于低延迟
// 要求。
func WithSerializable() OpOption {
	return func(op *Op) { op.serializable = true }
}

// WithKeysOnly使“Get”请求仅返回键，相应的
// 值将被忽略。
func WithKeysOnly() OpOption {
	return func(op *Op) { op.keysOnly = true }
}

// WithCountOnly使“Get”请求仅返回键的计数。
func WithCountOnly() OpOption {
	return func(op *Op) { op.countOnly = true }
}

// WithMinModRev过滤掉修改版本小于给定版本的Get键。
func WithMinModRev(rev int64) OpOption { return func(op *Op) { op.minModRev = rev } }

// WithMaxModRev过滤掉修改版本大于给定版本的Get键。
func WithMaxModRev(rev int64) OpOption { return func(op *Op) { op.maxModRev = rev } }

// WithMinCreateRev过滤掉创建版本小于给定版本的Get键。
func WithMinCreateRev(rev int64) OpOption { return func(op *Op) { op.minCreateRev = rev } }

// WithMaxCreateRev过滤掉创建版本大于给定版本的Get的键。
func WithMaxCreateRev(rev int64) OpOption { return func(op *Op) { op.maxCreateRev = rev } }

// WithFirstCreate获取请求范围内最早创建版本的键。
func WithFirstCreate() []OpOption { return withTop(SortByCreateRevision, SortAscend) }

// WithLastCreate获取请求范围内最新创建版本的键。
func WithLastCreate() []OpOption { return withTop(SortByCreateRevision, SortDescend) }

// WithFirstKey获取请求范围内词汇上的第一个密钥。
func WithFirstKey() []OpOption { return withTop(SortByKey, SortAscend) }

// WithLastKey获取请求范围内词汇上的最后一个密钥。
func WithLastKey() []OpOption { return withTop(SortByKey, SortDescend) }

// WithFirstRev获取请求范围内修改版本最早的密钥。
func WithFirstRev() []OpOption { return withTop(SortByModRevision, SortAscend) }

// WithLastRev获取请求范围内修改版本最新的密钥e、 
func WithLastRev() []OpOption { return withTop(SortByModRevision, SortDescend) }

// withTop在给定排序顺序的get前缀上获取第一个键
func withTop(target SortTarget, order SortOrder) []OpOption {
	return []OpOption{WithPrefix(), WithSort(target, order), WithLimit(1)}
}

// WITHPROGRESS NOTIFY使监视服务器在没有传入事件时每10分钟发送一次定期进度更新
// 。
// 进度更新在WatchResponse中没有事件。
func WithProgressNotify() OpOption {
	return func(op *Op) {
		op.progressNotify = true
	}
}

// WithCreatedNotify使watch server发送创建的事件。
func WithCreatedNotify() OpOption {
	return func(op *Op) {
		op.createdNotify = true
	}
}

// WithFilterPut丢弃来自watcher的PUT事件。
func WithFilterPut() OpOption {
	return func(op *Op) { op.filterPut = true }
}

// WithFilterDelete丢弃来自watcher的删除事件。
func WithFilterDelete() OpOption {
	return func(op *Op) { op.filterDelete = true }
}

// WithPrevKV在事件发生之前获取上一个键值对。如果上一个KV已经压缩，
// 将不返回任何内容。
func WithPrevKV() OpOption {
	return func(op *Op) {
		op.prevKV = true
	}
}

// WithFragment将接收带分段的原始监视响应。默认情况下，
// 分段处于禁用状态。如果启用分段，
// etcd watch server将在发送到客户端之前分割监视响应
// 当监视事件的总大小超出服务器端请求限制。
// 默认服务器端请求限制为1.5 MiB，可将其配置为“-max request bytes”标志值+gRPC开销512字节。
// 请参阅“etcdserver/api/v3rpc/watch.go”有关详细信息，请参见。
func WithFragment() OpOption {
	return func(op *Op) { op.fragment = true }
}

// WithIgnoreValue使用其当前值更新密钥。
// 此选项不能与非空值组合。
// 如果密钥不存在，则返回错误。
func WithIgnoreValue() OpOption {
	return func(op *Op) {
		op.ignoreValue = true
	}
}

// WithIgnoreLease使用其当前租约更新密钥。
// 此选项可以为ot与lease组合。
// 如果密钥不存在，则返回错误。
func WithIgnoreLease() OpOption {
	return func(op *Op) {
		op.ignoreLease = true
	}
}

// LeaseOp表示lease可以执行的操作。
type LeaseOp struct {
	id LeaseID

	// 对于TimeToLive 
	attachedKeys bool
}

// LeaseOption配置lease操作。
type LeaseOption func(*LeaseOp)

func (op *LeaseOp) applyOpts(opts []LeaseOption) {
	for _, opt := range opts {
		opt(op)
	}
}

// WithAttachedKeys使TimeToLive列出附加到的密钥如果在给定的选项中调用WithPrefix选项，则给定的租约ID.
func WithAttachedKeys() LeaseOption {
	return func(op *LeaseOp) { op.attachedKeys = true }
}

func toLeaseTimeToLiveRequest(id LeaseID, opts ...LeaseOption) *pb.LeaseTimeToLiveRequest {
	ret := &LeaseOp{id: id}
	ret.applyOpts(opts)
	return &pb.LeaseTimeToLiveRequest{ID: int64(id), Keys: ret.attachedKeys}
}

// IsOptsWithPrefix返回true。如果在给定的选项中调用WithFromKey选项，则
func IsOptsWithPrefix(opts []OpOption) bool {
	ret := NewOp()
	for _, opt := range opts {
		opt(ret)
	}

	return ret.isOptsWithPrefix
}

// IsOptsWithFromKey返回true。
func IsOptsWithFromKey(opts []OpOption) bool {
	ret := NewOp()
	for _, opt := range opts {
		opt(ret)
	}

	return ret.isOptsWithFromKey
}
